using System;
using System.Collections.Generic;
using System.Text;

namespace NBusiness.Query.SqlServer
{
    public class SqlSelect : SqlEvaluatable, IQuery
    {
        #region Members and fields
        private List<ISelectable> _select = new List<ISelectable>();
        private List<IFrom> _from = new List<IFrom>();
        private IExpression _where;
        private IList<IColumn> _groupBy = new List<IColumn>();
        private IOrder _orderBy;

        private string _alias;
        /// <summary>
        /// Set this value to alias a sub query
        /// </summary>
        public string Alias
        {
            get { return _alias; }
            set { _alias = value; }
        }
        #endregion

        #region Constructors
        public SqlSelect() 
        { }
        public SqlSelect(string alias)
        {
            _alias = alias;
        }
        #endregion

        #region IRelationalElement Members

        public override string ToSql()
        {
            return ToSql(new SqlQueryContext());
        }
        public override string ToSql(IQueryContext context)
        {
            StringBuilder builder = new StringBuilder("SELECT ");
            BuildSelect(builder, context);

            builder.Append(" FROM ");
            BuildFrom(builder, context);

            if (_where != null)
            {
                builder.Append(" WHERE ");
                builder.Append(_where.ToSql(context));
            }

            if (_groupBy.Count > 0)
            {
                builder.Append(" GROUP BY ");
                BuildGroupBy(builder, context);
            }
            if (_orderBy != null)
                builder.Append(" " + _orderBy.ToSql(context));

            if (!string.IsNullOrEmpty(_alias))
            {
                builder.Insert(0, "(");
                builder.Append(")");

                if (_alias != null)
                {
                    builder.Append(" AS [");
                    builder.Append(_alias);
                    builder.Append("]");
                }
            }

            return builder.ToString();
        }

        private void BuildSelect(StringBuilder builder, IQueryContext context)
        {
            if (_select.Count == 0)
                builder.Append("*");
            else
            {
                for (int x = 0; x < _select.Count; x++)
                {
                    if (x > 0)
                        builder.Append(", ");

                    builder.Append(_select[x].ToSql(context));
                }
            }
        }
        private void BuildFrom(StringBuilder builder, IQueryContext context)
        {
            if (_from == null)
                throw new ArgumentNullException("From");
            if (_from.Count == 0)
                throw new ArgumentOutOfRangeException("From", Properties.Resources.SqlSelectFromMissing);

            for (int x = 0; x < _from.Count; x++)
            {
                if (x > 0)
                {
                    if (!(_from[x] is IJoin))
                        throw new ArgumentException(Properties.Resources.SqlSelectFromNotJoin);

                    builder.Append(" "); // Multiple froms will be joins
                }

                builder.Append(_from[x].ToSql(context));
            }
        }
        private void BuildGroupBy(StringBuilder builder, IQueryContext context)
        {
            for (int x=0; x < _groupBy.Count; x++)
            {
                if (x > 0)
                    builder.Append(", ");

                if (_groupBy[x].Parent != null && 
                    _groupBy[x].Parent is ITable &&
                    (_groupBy[x].Parent as ITable).Alias != null)
                {
                    builder.Append("[" + (_groupBy[x].Parent as ITable).Alias + "].");
                }
                else if (_groupBy[x].Parent != null &&
                    _groupBy[x].Parent is ITable)
                {
                    builder.Append("[" + (_groupBy[x].Parent as ITable).Name + "].");
                }

                builder.Append("[" + _groupBy[x].Name + "]");
            }
        }

        #endregion

        #region ISource Members

        public IQuery OrderBy(IOrder orderBy)
        {
            _orderBy = orderBy;
            return this;
        }
        public IQuery OrderBy(SortOrder order, params IColumn[] columns)
        {
            _orderBy = new SqlOrderBy(order, columns);
            return this;
        }

        public IQuery GroupBy(params IColumn[] groupBy)
        {
            _groupBy = new List<IColumn>(groupBy);
            return this;
        }

        public IQuery Where(IExpression where)
        {
            _where = where;
            return this;
        }

        public IQuery Select(params ISelectable[] selectable)
        {
            _select = new List<ISelectable>(selectable);
            return this;
        }

        public IQuery As(string alias)
        {
            _alias = alias;
            return this;
        }

        public IQuery Join(IJoin join)
        {
            List<IFrom> froms = new List<IFrom>(_from);
            froms.Add(join);
            _from = froms;
            return this;
        }

        #endregion

        #region IQuery methods

        public IQuery From(params IFrom[] from)
        {
            _from = new List<IFrom>(from);
            return this;
        }
        
        #endregion

        #region Object overrides
        public override string ToString()
        {
            return ToSql();
        }
        #endregion
    }
}
